#include "physics_calc.h"

PhysicsCalc::PhysicsCalc(void)
{
	gravity = new Vector2D(0.0f, -0.005f);
	collisionDetector = new CollisionDetector();
}

PhysicsCalc::~PhysicsCalc(void)
{
	delete gravity;
	gravity = null;
}

/* aggiorna le entita' e gestisce le collisioni*/
void PhysicsCalc::updateEntities(World *w, double delta)
{
	CollisionInfo *curCollision;
	int entitiesCount = w->getEntityCount();

	/* scorre tutte le entita' */
	for (int i = 0; i < entitiesCount; ++i)
	{			
		for (int j = i+1; j < entitiesCount; ++j)
		{			
			curCollision = collisionDetector->resolveCollision(w->getEntity(i),w->getEntity(j));

			if (curCollision != '\0' && curCollision->willCollide)
			{	
				/* COLLISIONE GIOCATORE - AREA */
				if (((w->getEntity(i)->m_type == TYPE_PLAYER && w->getEntity(j)->m_type == TYPE_MAP_AREA) || (w->getEntity(j)->m_type == TYPE_PLAYER && w->getEntity(i)->m_type == TYPE_MAP_AREA)) && curCollision->m_type == ORTHO_COLLISION)
				{					
					WorldPlayer *player = (WorldPlayer*)((w->getEntity(i)->m_type == TYPE_PLAYER)?(w->getEntity(i)):(w->getEntity(j)));
					WorldArea *area = (WorldArea*)((w->getEntity(i)->m_type == TYPE_PLAYER)?(w->getEntity(j)):(w->getEntity(i)));
					PlayerOrthoCollision *coll = (PlayerOrthoCollision*)curCollision;
					
					if (coll->m_side == LEFT)
					{					
						//player->x = (area->vertexs[0] < area->vertexs[2])?(area->vertexs[2]):(area->vertexs[0]) + player->width/2;

					}
					else if (coll->m_side == RIGHT)
					{														
						//player->x = (area->vertexs[0] < area->vertexs[2])?(area->vertexs[0]):(area->vertexs[2]) - player->width/2;

					}
					else if (coll->m_side == DOWN)
					{											
						//printf("DOWN\r\n");fflush(stdout);
						/* aggiustiamo la posizione della collisione */
						player->y += coll->m_depth;
						/* attrito + fermiamo il giocatore*/
						player->velocity = Vector2D((player->m_movement)?(player->velocity.getX()):(player->velocity.getX()*0.50f), 0.0f);
						/* applichiamo una forza opposta alla gravita' che lo tiene fermo */
						player->addForce(-player->m_gravity);
						
					}
					else if (coll->m_side == UP)
					{					
						//printf("UP\r\n");fflush(stdout);
						/* aggiustiamo la posizione della collisione */
						player->y -= coll->m_depth;
						/* fermiamo il giocatore */
						player->velocity = Vector2D(player->velocity.getX(), 0.0f);
						/* applichiamo una forza opposta alla gravita' che lo tiene fermo */
						//player->addForce(-player->m_gravity);
					}
				}
				else if (((w->getEntity(i)->m_type == TYPE_PROJECTILE && w->getEntity(j)->m_type == TYPE_MAP_AREA) || (w->getEntity(j)->m_type == TYPE_PROJECTILE && w->getEntity(i)->m_type == TYPE_MAP_AREA)) && curCollision->m_type == ORTHO_COLLISION)
				{
					WorldArea *area;
					WorldProjectile *projectile;
					ProjectileOrthoCollision *coll = (ProjectileOrthoCollision*)curCollision;
					
					if (w->getEntity(i)->m_type == TYPE_PROJECTILE)
					{
						area = (WorldArea*)w->getEntity(j);
						projectile = (WorldProjectile*)w->getEntity(i);
					}
					else
					{
						area = (WorldArea*)w->getEntity(i);
						projectile = (WorldProjectile*)w->getEntity(j);
					}

					gamePlay->collisionProjectileArea(projectile, area, coll->m_x, coll->m_y);
					//gamePlay->collisionProjectileArea(area, projectile, coll->m_x, coll->m_y);
				}
					
				delete curCollision;
				curCollision = '\0';

			}
		}
		
		updateEntity(w->getEntity(i), delta);
	}
}

void PhysicsCalc::updateEntity(WorldEntity *entity, double delta)
{	
	switch(entity->m_type)
	{
		case TYPE_PLAYER: updateEntity((WorldPlayer*)entity, delta); break; 
		/* forse inutile: le aree non si aggiornano */
		//case TYPE_MAP_AREA: updateEntity((WorldArea*)entity); break;
		case TYPE_PROJECTILE: updateEntity((WorldProjectile*)entity, delta); break;
	}
	
}

/* TODO: non va bene cosi', deve diventare piu' serio e generico :/ */
void PhysicsCalc::updateEntity(WorldPlayer *entity, double delta)
{
	/*if (entity->acceleration.isNull())
		entity->acceleration.add(gravity);*/
	
	entity->integrate(delta);
}

/* forse inutile: le aree non si aggiornano */
void PhysicsCalc::updateEntity(WorldArea *entity, double delta)
{	

}

void PhysicsCalc::updateEntity(WorldProjectile *entity, double delta)
{
	//TODO: gestire aggiornamento proiettili
	entity->integrate(delta);

}
